#include <iostream>
#include <bitset>
#include <string>

#include <stdlib.h>
#include <sys/timeb.h>


using namespace std;


// типы базовых адресов, задаются джамперами.
// дают возможность поставить до 3х устрйоств
enum base_type { 
    base_type1 = 0x200,
    base_type2 = 0x220,
    base_type3 = 0x300 
};


// тип мультиплексора
enum multiplex_mode {
    differential = 8,
    potential    = 16 
};


// смещения
enum shifts {
    dac_shift =  0x04,
    adc_channel_shift = 0x02,
    adc_start_shift   = 0x00
};

class NVL {
    public:
        NVL(base_type, multiplex_mode);

        void DAC(bitset<12> data);              // 1 канал, 12 бит
        bitset<12> ADC(unsigned int channel);   // 8 или 16 каналов, 12 бит

        void write_to_port(bitset<16> address, bitset<16> data);
        bitset<16> read_from_port(bitset<16> address);

        ~NVL();
    private:
        base_type base;           // тип базового адреса
        multiplex_mode multiplex; // тип мультиплексора
};

bitset<16> NVL::read_from_port(bitset<16> address) {
    unsigned int rand_value = rand();
    bitset<16> data_from_port;
    data_from_port.set();

    data_from_port.reset(11); // for test
    data_from_port.reset(12); // 12 разряд -- показывает конец цикла преобразования АЦП (когда 0)

    cout 
        << "Прочли значение: "
        << data_from_port.to_string<char>() 
        << " "
        << "по адресу: " 
        << std::hex << address.to_ulong() 
        << endl;

    return data_from_port;
}

void NVL::DAC(bitset<12> data) {    // 1 канал
    bitset<16> data_to_port;
    data_to_port.reset();

    data_to_port = data.to_ulong();

    write_to_port( this->base + dac_shift, data_to_port);
}


// заглушка на запись в порт
void NVL::write_to_port(bitset<16> address, bitset<16> data) {
    cout 
        << "Записываем значение: "
        << data.to_string<char>() 
        << " "
        << "по адресу: " 
        << std::hex << address.to_ulong() 
        << endl;
}

bitset<12> NVL::ADC(unsigned int channel) {  // 8 или 16 каналов
    if (channel > multiplex) {
        cout<<"Такого канала нету!"<<endl;
        exit(1);
    }

    write_to_port( this->base + adc_channel_shift, channel ); // указываем необходимый канал мультиплексора 
    write_to_port( this->base + adc_start_shift, 1234 );      // запускаем АЦП программно, 1234 -- любое число
    
    bitset<16> data_from_port;

    // ждём окончания цикла преобразования
    do {
        data_from_port = read_from_port( this->base + adc_start_shift );
        sleep(1);
    } while (data_from_port.test(12));

    unsigned int result = data_from_port.to_ulong();

    return result; // срежет лишние 4 бита
}

NVL::NVL(base_type base_param, multiplex_mode multiples_param) :
    base (base_param),
    multiplex(multiples_param) {

        this->DAC(2047); // выставляем нулевое значение выхода ЦАП
}

NVL::~NVL() {

}


int main() {
    unsigned int channel = 8;
    struct timeb tp1, tp2; 
    unsigned long int difference = 0;    

    NVL* device = new NVL(base_type3, differential);

    ftime(&tp1);
    ftime(&tp2);

    int i = 0;

    do {
        difference = tp2.time * 1000 + tp2.millitm - tp1.millitm - tp1.time * 1000;
        ftime(&tp2);
        printf("d");
    
        if (++i > 5000) // выход по какому-либо условию
            break;
    } while (difference < 5);


    printf("\n%ld msec left!", difference);

    if ( difference < 4 )
        cout << device->ADC(channel).to_string<char>() << endl;
    else 
        device->DAC(difference);

    return 0;
}
